import re
from django.utils.functional import memoize
from django.core.urlresolvers import get_callable, get_mod_func
from django.utils.datastructures import MultiValueDict


_resolver_cache = {} # Maps mailconf modules to RegexURLResolver instances.

class MailerDoesNotExist(Exception):
  pass

class RecipientNotFound(Exception):
  pass

def get_resolver(mailconf):
    if mailconf is None:
        from django.conf import settings
        mailconf = settings.ROOT_EMAILCONF
    return RegexMailResolver(r'', mailconf)
get_resolver = memoize(get_resolver, _resolver_cache, 1)

class RegexMailPattern(object):
    def __init__(self, regex, callback, default_args=None, name=None):
        self.regex = re.compile(regex, re.UNICODE)
        if callable(callback):
            self._callback = callback
        else:
            self._callback = None
            self._callback_str = callback
        self.default_args = default_args or {}
        self.name = name

    def __repr__(self):
        return '<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern)

    def add_prefix(self, prefix):
        """
        Adds the prefix string to a string-based callback.
        """
        if not prefix or not hasattr(self, '_callback_str'):
            return
        self._callback_str = prefix + '.' + self._callback_str

    def resolve(self, message):
#        match = self.regex.search(message)
        match = None
        for to in message.to:
          match = self.regex.search(to)
        if match:
            # If there are any named groups, use those as kwargs, ignoring
            # non-named groups. Otherwise, pass all non-named arguments as
            # positional arguments.
            kwargs = match.groupdict()
            if kwargs:
                args = ()
            else:
                args = match.groups()
            # In both cases, pass any extra_kwargs as **kwargs.
            kwargs.update(self.default_args)

            return self.callback, args, kwargs

    def _get_callback(self):
        if self._callback is not None:
            return self._callback
        try:
            self._callback = get_callable(self._callback_str)
        except ImportError, e:
            mod_name, _ = get_mod_func(self._callback_str)
            raise MailerDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e))
        except AttributeError, e:
            mod_name, func_name = get_mod_func(self._callback_str)
            raise MailerDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e))
        return self._callback
    callback = property(_get_callback)


class RegexMailResolver(object):
  def __init__(self, regex, mailconf_name, default_kwargs=None):
        # regex is a string representing a regular expression.
        # urlconf_name is a string representing the module containing urlconfs.
        self.regex = re.compile(regex, re.UNICODE)
        self.mailconf_name = mailconf_name
        self.callback = None
        self.default_kwargs = default_kwargs or {}
        self._reverse_dict = MultiValueDict()

  def __repr__(self):
        return '<%s %s %s>' % (self.__class__.__name__, self.mailconf_name, self.regex.pattern)

  def _get_reverse_dict(self):
    raise NotImplementedError

  reverse_dict = property(_get_reverse_dict)

  def resolve(self, message):
        tried = []
        #match = self.regex.search(message)
        match = None
        recipient = ''
        for to in message.to:
          match = self.regex.search(to)
          if match:
            recipient = to
        if match:
            new_recip = recipient[match.end():]
            for pattern in self.mailconf_module.mailboxpatterns:
                try:
                    sub_match = pattern.resolve(message)
#                except Resolver404, e:
#                    tried.extend([(pattern.regex.pattern + '   ' + t) for t in e.args[0]['tried']])
#                else:
                except:
                  raise
                else:
                    if sub_match:
                        sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])
                        sub_match_dict.update(self.default_kwargs)
                        for k, v in sub_match[2].iteritems():
                            sub_match_dict[smart_str(k)] = v
                        return sub_match[0], sub_match[1], sub_match_dict
                    tried.append(pattern.regex.pattern)
            raise RecipientNotFound, {'tried': tried, 'path': new_recip}


  def _get_mailconf_module(self):
        try:
            return self._mailconf_module
        except AttributeError:
            self._mailconf_module = __import__(self.mailconf_name, {}, {}, [''])
            return self._mailconf_module
  mailconf_module = property(_get_mailconf_module)

  def _get_mail_patterns(self):
        return self.mailconf_module.mailpatterns
  mail_patterns = property(_get_mail_patterns)


def resolve(message, mailconf=None):
    return get_resolver(mailconf).resolve(message)

def reverse(viewname, mailconf=None, args=None, kwargs=None, prefix=None):
    raise NotImplementedError

def clear_caches():
    global _resolver_cache
    global _callable_cache
    _resolver_cache.clear()
    _callable_cache.clear()

